#include "stdafx.h"
#include "renderer.h"
#include "common/geoGen.h"
#include "common/app.h"
#include "common/utility.h"

Renderer::Renderer(UINT width, UINT height, std::wstring name) :
	m_width(width), m_height(height),
	m_title(name),
	m_viewport(0.0f, 0.0f, static_cast<float>(m_width), static_cast<float>(m_height)),
	m_scissorRect(0, 0, static_cast<LONG>(m_width), static_cast<LONG>(m_height)),
	m_aspectRatio(static_cast<float>(1.0 * m_width / m_height))
{
	XMStoreFloat4x4(&m_proj, XMMatrixPerspectiveFovLH(0.25f * XM_PI, m_aspectRatio, 1.0f, 1000.0f));
	m_timer = std::make_unique<GameTimer>();
}

Renderer::~Renderer()
{
}

void Renderer::OnInit()
{
	InitHelper initHelper(this);
	initHelper.init_adapter();
	initHelper.init_fence();
	initHelper.init_commandQLA();
	initHelper.init_swapChain();
	initHelper.init_dspHeaps();
	initHelper.init_rtv();
	initHelper.init_dsv();

	//srv heap
	initHelper.init_renderItems();
	{
		D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
		srvHeapDesc.NumDescriptors = m_textures.size();
		srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
		srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
		ThrowIfFailed(m_device->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(&m_srvHeap)));
		//srv desps
		CD3DX12_CPU_DESCRIPTOR_HANDLE hDescriptor(m_srvHeap->GetCPUDescriptorHandleForHeapStart());
		for (auto& x : m_textures)
		{
			auto tex_res = x->Resource;
			D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
			srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
			srvDesc.Format = tex_res->GetDesc().Format;
			srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
			srvDesc.Texture2D.MostDetailedMip = 0;
			srvDesc.Texture2D.MipLevels = tex_res->GetDesc().MipLevels;
			srvDesc.Texture2D.ResourceMinLODClamp = 0.0f;
			m_device->CreateShaderResourceView(tex_res.Get(), &srvDesc, hDescriptor);
			hDescriptor.Offset(1, m_srvDescriptorSize);
		}
	}

	//check MSAA
	/*
	D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msQualityLevels;
	msQualityLevels.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
	msQualityLevels.SampleCount = 4;
	msQualityLevels.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE;
	msQualityLevels.NumQualityLevels = 0;
	ThrowIfFailed(m_device->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
		&msQualityLevels, sizeof(msQualityLevels)));
	UINT m4xMsaaQuality = msQualityLevels.NumQualityLevels;
	assert(m4xMsaaQuality > 0 && "Unexpected MSAA quality level.");
	*/

	//build frameresource
	for (int i = 0; i < NumFrameResource; i++)
		m_frameResources.push_back(std::make_unique<FrameResource>(m_device.Get(), 1, m_allRitems.size(), (UINT)m_materials.size()));

	// root signature
	CD3DX12_DESCRIPTOR_RANGE texTable;
	texTable.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0);

	CD3DX12_ROOT_PARAMETER slotRootParameter[4];
	slotRootParameter[0].InitAsDescriptorTable(1, &texTable, D3D12_SHADER_VISIBILITY_PIXEL);
	slotRootParameter[1].InitAsConstantBufferView(0);
	slotRootParameter[2].InitAsConstantBufferView(1);
	slotRootParameter[3].InitAsConstantBufferView(2);

	//samplers
	auto staticSamplers = GetStaticSamplers();

	CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc;
	rootSigDesc.Init(4, slotRootParameter, (UINT)staticSamplers.size(), staticSamplers.data(),
		D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

	ComPtr<ID3DBlob> serializedRootSig , errorBlob;
	ThrowIfFailed(D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1, serializedRootSig.GetAddressOf(),
		errorBlob.GetAddressOf()));
	if (errorBlob != nullptr)
	{
		OutputDebugStringA((char*)errorBlob->GetBufferPointer());
	}
	ThrowIfFailed(m_device->CreateRootSignature(0, serializedRootSig->GetBufferPointer(), serializedRootSig->GetBufferSize(),
		IID_PPV_ARGS(&m_rootSignature)));

	//	PSO
	{
#if defined(_DEBUG)
		// Enable better shader debugging with the graphics debugging tools.
		UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#else
		UINT compileFlags = 0;
#endif
		ComPtr<ID3DBlob> vs, ps;
		ComPtr<ID3DBlob> errors;
		ThrowIfFailed(D3DCompileFromFile(L"shaders/shader.hlsl", nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, 
			"VSMain", "vs_5_0", compileFlags, 0, &vs, &errors));
		ThrowIfFailed(D3DCompileFromFile(L"shaders/shader.hlsl", nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, 
			"PSMain", "ps_5_0", compileFlags, 0, &ps, &errors));
		
		D3D12_INPUT_ELEMENT_DESC inputElementDescs[] =
		{
			{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
			{"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
			{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
		};
		D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
		psoDesc.InputLayout = { inputElementDescs, _countof(inputElementDescs) };
		psoDesc.pRootSignature = m_rootSignature.Get();
		psoDesc.VS = CD3DX12_SHADER_BYTECODE(vs.Get());
		psoDesc.PS = CD3DX12_SHADER_BYTECODE(ps.Get());
		psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
		//psoDesc.RasterizerState.FillMode = D3D12_FILL_MODE_WIREFRAME;
		psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
		psoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
		psoDesc.SampleMask = UINT_MAX;
		psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
		psoDesc.NumRenderTargets = 1;
		psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
		psoDesc.SampleDesc.Count = 1;
		psoDesc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
		
		ThrowIfFailed(m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_pso)));


		D3D12_RENDER_TARGET_BLEND_DESC transparencyBlendDesc;
		transparencyBlendDesc.BlendEnable = true;
		transparencyBlendDesc.LogicOpEnable = false;
		transparencyBlendDesc.SrcBlend = D3D12_BLEND_SRC_ALPHA;
		transparencyBlendDesc.DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
		transparencyBlendDesc.BlendOp = D3D12_BLEND_OP_ADD;
		transparencyBlendDesc.SrcBlendAlpha = D3D12_BLEND_ONE;
		transparencyBlendDesc.DestBlendAlpha = D3D12_BLEND_ZERO;
		transparencyBlendDesc.BlendOpAlpha = D3D12_BLEND_OP_ADD;
		transparencyBlendDesc.LogicOp = D3D12_LOGIC_OP_NOOP;
		transparencyBlendDesc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;

		psoDesc.BlendState.RenderTarget[0] = transparencyBlendDesc;
		ThrowIfFailed(m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_pso_trans)));

	}

	//exec initialization list
	ThrowIfFailed(m_commandList->Close());
	ID3D12CommandList* ppCmdList[] = { m_commandList.Get() };
	m_commandQueue->ExecuteCommandLists(_countof(ppCmdList), ppCmdList);

	FlushCommandQueue();
	for(auto& geos : m_meshes)
		geos.second->DisposeUploaders();
}

void Renderer::OnUpdate()
{
	UpdateCamera();

	// frame resource
	m_currFrameResourceIndex = (m_currFrameResourceIndex + 1) % NumFrameResource;
	
	UINT FRfence = m_frameResources[m_currFrameResourceIndex]->fence;
	if (FRfence && m_fence->GetCompletedValue() < FRfence)
	{
		ThrowIfFailed(m_fence->SetEventOnCompletion(FRfence, m_fenceEvent));
		WaitForSingleObject(m_fenceEvent, INFINITE);
	}

	UpdateObjectCBs();
	UpdateMaterialCBs();
	UpdateMainPassCB();
}

void Renderer::UpdateCamera()
{
	m_eyePos.x = m_radius * sinf(m_phi) * cosf(m_theta);
	m_eyePos.z = m_radius * sinf(m_phi) * sinf(m_theta);
	m_eyePos.y = m_radius * cosf(m_phi);

	XMVECTOR pos = XMVectorSet(m_eyePos.x, m_eyePos.y, m_eyePos.z, 1.0f);
	XMVECTOR target = XMVectorZero();
	XMVECTOR up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);

	XMMATRIX view = XMMatrixLookAtLH(pos, target, up);
	XMStoreFloat4x4(&m_view, view);
}

void Renderer::UpdateObjectCBs()
{
	auto curObjCB = m_frameResources[m_currFrameResourceIndex]->objCB.get();
	for (auto& e : m_allRitems)
	{
		if (e->numFramesDirty > 0)
		{
			XMMATRIX world = XMLoadFloat4x4(&e->World);
			XMMATRIX texTransform = XMLoadFloat4x4(&e->TexTransform);

			ObjectConstants objConsts;
			XMStoreFloat4x4(&objConsts.World, XMMatrixTranspose(world));
			XMStoreFloat4x4(&objConsts.TexTransform, XMMatrixTranspose(texTransform));

			curObjCB->CopyData(e->ObjCBIndex, objConsts);

			e->numFramesDirty--;
		}
	}
}

void Renderer::UpdateMainPassCB()
{
	XMMATRIX view = XMLoadFloat4x4(&m_view);
	XMMATRIX proj = XMLoadFloat4x4(&m_proj);

	XMMATRIX viewProj = XMMatrixMultiply(view, proj);
	XMMATRIX invView = XMMatrixInverse(&XMMatrixDeterminant(view), view);
	XMMATRIX invProj = XMMatrixInverse(&XMMatrixDeterminant(proj), proj);
	XMMATRIX invViewProj = XMMatrixInverse(&XMMatrixDeterminant(viewProj), viewProj);

	XMStoreFloat4x4(&m_mainPassCB.View, XMMatrixTranspose(view));
	XMStoreFloat4x4(&m_mainPassCB.InvView, XMMatrixTranspose(invView));
	XMStoreFloat4x4(&m_mainPassCB.Proj, XMMatrixTranspose(proj));
	XMStoreFloat4x4(&m_mainPassCB.InvProj, XMMatrixTranspose(invProj));
	XMStoreFloat4x4(&m_mainPassCB.ViewProj, XMMatrixTranspose(viewProj));
	XMStoreFloat4x4(&m_mainPassCB.InvViewProj, XMMatrixTranspose(invViewProj));

	m_mainPassCB.EyePosW = m_eyePos;
	m_mainPassCB.RenderTargetSize = XMFLOAT2((float)m_width, (float)m_height);
	m_mainPassCB.InvRenderTargetSize = XMFLOAT2(1.0f / m_width, 1.0f / m_height);
	m_mainPassCB.NearZ = 1.0f;
	m_mainPassCB.FarZ = 1000.0f;
	m_mainPassCB.TotalTime = m_timer->TotalTime();
	m_mainPassCB.DeltaTime = m_timer->DeltaTime();

	m_mainPassCB.AmbientLight = { 0.25f, 0.25f, 0.25f, 1.0f};
	m_mainPassCB.Lights[0].Direction = { 0.57735f, -0.57735f, 0.57735f };
	m_mainPassCB.Lights[0].Strength = { 0.8f, 0.8f, 0.8f };
	m_mainPassCB.Lights[1].Direction = { -0.57735f, -0.57735f, 0.57735f };
	m_mainPassCB.Lights[1].Strength = { 0.4f, 0.4f, 0.4f };
	m_mainPassCB.Lights[2].Direction = { 0.0f, -0.707f, -0.707f };
	m_mainPassCB.Lights[2].Strength = { 0.2f, 0.2f, 0.2f };
/**/
	auto currPassCB = m_frameResources[m_currFrameResourceIndex]->passCB.get();
	currPassCB->CopyData(0, m_mainPassCB);
}

void Renderer::UpdateMaterialCBs()
{
	auto currMaterialCB = m_frameResources[m_currFrameResourceIndex]->matCB.get();
	for (auto& e : m_materials)
	{
		// Only update the cbuffer data if the constants have changed.  If the cbuffer
		// data changes, it needs to be updated for each FrameResource.
		Material* mat = e.second.get();
		if (mat->NumFramesDirty > 0)
		{
			XMMATRIX matTransform = XMLoadFloat4x4(&mat->MatTransform);

			MaterialConstants matConstants;
			matConstants.DiffuseAlbedo = mat->DiffuseAlbedo;
			matConstants.FresnelR0 = mat->FresnelR0;
			matConstants.Roughness = mat->Roughness;
			XMStoreFloat4x4(&matConstants.MatTransform, XMMatrixTranspose(matTransform));

			currMaterialCB->CopyData(mat->MatCBIndex, matConstants);

			// Next FrameResource need to be updated too.
			mat->NumFramesDirty--;
		}
	}
}

void Renderer::OnRender()
{
	PopulateCommandList();

	ID3D12CommandList* ppCmdList[] = { m_commandList.Get() };
	m_commandQueue->ExecuteCommandLists(_countof(ppCmdList), ppCmdList);
	
	ThrowIfFailed(m_swapChain->Present(0, 0));

	m_frameResources[m_currFrameResourceIndex]->fence = ++m_fenceValue;
	ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), m_fenceValue));

	m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
}

void Renderer::OnDestroy()
{
	FlushCommandQueue();
	CloseHandle(m_fenceEvent);
	m_timer = nullptr;
}

void Renderer::OnKeyDown(UINT8 keyNum)
{

}

void Renderer::OnKeyUp(UINT8 keyNum)
{

}

void Renderer::OnMouseDown(WPARAM wParam, int x, int y)
{
}

void Renderer::OnMouseUp(WPARAM wParam, int x, int y)
{

}

void Renderer::OnMouseMove(WPARAM wParam, int x, int y)
{
	if ( (wParam & MK_LBUTTON) != 0)
	{
		float dx = XMConvertToRadians(0.25f * static_cast<float>(ms_x - x));
		float dy = XMConvertToRadians(0.25f * static_cast<float>(ms_y - y));
		m_theta += dx;
		m_phi += dy;
		m_phi = clamp(m_phi, 0.1f, XM_PI - 0.1f);
	}
	else if( (wParam & MK_RBUTTON) != 0)
	{
		float dx = 0.05f * static_cast<float>(x - ms_x);
		float dy = 0.05f * static_cast<float>(y - ms_y);
		m_radius += dx - dy;
		m_radius = clamp(m_radius, 3.0f, 50.0f);
	}
	ms_x = x;
	ms_y = y;
}

void Renderer::SetCustomWindowText(LPCWSTR text)
{
	std::wstring windowText = m_title + L": " + text;
	SetWindowText(App::GetHwnd(), windowText.c_str());
}

void Renderer::PopulateCommandList()
{
	auto cmdListAlloc = m_frameResources[m_currFrameResourceIndex]->cmdListAlloc;

	ThrowIfFailed(cmdListAlloc->Reset());
	ThrowIfFailed(m_commandList->Reset(cmdListAlloc.Get(), m_pso.Get()));

	// Rasterizing
	m_commandList->RSSetViewports(1, &m_viewport);
	m_commandList->RSSetScissorRects(1, &m_scissorRect);

	m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_frameIndex].Get(),
			D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));

	D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = CD3DX12_CPU_DESCRIPTOR_HANDLE(m_rtvHeap->GetCPUDescriptorHandleForHeapStart(), m_frameIndex, m_rtvDescriptorSize);
	D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = m_dsvHeap->GetCPUDescriptorHandleForHeapStart();

	// Clear the back buffer and depth buffer
	const float clearColor[4] = { 0.2f, 0.2f, 0.2f, 1.0f };
	m_commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
	m_commandList->ClearDepthStencilView(dsvHandle, D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);

	// Specify the buffers we are going to render to.
	m_commandList->OMSetRenderTargets(1, &rtvHandle, true, &dsvHandle);
	
	ID3D12DescriptorHeap* descriptorHeaps[] = { m_srvHeap.Get() };
	m_commandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);

	m_commandList->SetGraphicsRootSignature(m_rootSignature.Get());
	auto passCB = m_frameResources[m_currFrameResourceIndex]->passCB->Resource();
	m_commandList->SetGraphicsRootConstantBufferView(2, passCB->GetGPUVirtualAddress());

	// IA
	// draw items
	UINT objCBByteSize = D3DUtil::CalConstBufferSize(sizeof(ObjectConstants));
	UINT matCBByteSize = D3DUtil::CalConstBufferSize(sizeof(MaterialConstants));

	auto objectCB = m_frameResources[m_currFrameResourceIndex]->objCB->Resource();
	auto matCB = m_frameResources[m_currFrameResourceIndex]->matCB->Resource();

	for (const auto& item : m_opaqueRitems)
	{
		m_commandList->IASetVertexBuffers(0, 1, &item->Geo->VertexBufferView());
		m_commandList->IASetIndexBuffer(&item->Geo->IndexBufferView());
		m_commandList->IASetPrimitiveTopology(item->m_primType);

		//per CBV
		D3D12_GPU_VIRTUAL_ADDRESS objCBaddr = objectCB->GetGPUVirtualAddress() + item->ObjCBIndex * objCBByteSize;
		D3D12_GPU_VIRTUAL_ADDRESS matCBaddr = matCB->GetGPUVirtualAddress() + item->Mat->MatCBIndex * matCBByteSize;
		
		CD3DX12_GPU_DESCRIPTOR_HANDLE tex(m_srvHeap->GetGPUDescriptorHandleForHeapStart());
		tex.Offset(item->Mat->DiffuseSrvHeapIndex, m_srvDescriptorSize);
		
		m_commandList->SetGraphicsRootDescriptorTable(0, tex);
		m_commandList->SetGraphicsRootConstantBufferView(1, objCBaddr);
		m_commandList->SetGraphicsRootConstantBufferView(3, matCBaddr);
		m_commandList->DrawIndexedInstanced(item->indexCount, 1, 
				item->startIndexLocation, item->baseVertexLocation, 0);
	}

	m_commandList->SetPipelineState(m_pso_trans.Get());
	for (const auto& item : m_transparentRitems)
	{
		m_commandList->IASetVertexBuffers(0, 1, &item->Geo->VertexBufferView());
		m_commandList->IASetIndexBuffer(&item->Geo->IndexBufferView());
		m_commandList->IASetPrimitiveTopology(item->m_primType);

		//per CBV
		D3D12_GPU_VIRTUAL_ADDRESS objCBaddr = objectCB->GetGPUVirtualAddress() + item->ObjCBIndex * objCBByteSize;
		D3D12_GPU_VIRTUAL_ADDRESS matCBaddr = matCB->GetGPUVirtualAddress() + item->Mat->MatCBIndex * matCBByteSize;

		CD3DX12_GPU_DESCRIPTOR_HANDLE tex(m_srvHeap->GetGPUDescriptorHandleForHeapStart());
		tex.Offset(item->Mat->DiffuseSrvHeapIndex, m_srvDescriptorSize);

		m_commandList->SetGraphicsRootDescriptorTable(0, tex);
		m_commandList->SetGraphicsRootConstantBufferView(1, objCBaddr);
		m_commandList->SetGraphicsRootConstantBufferView(3, matCBaddr);
		m_commandList->DrawIndexedInstanced(item->indexCount, 1,
			item->startIndexLocation, item->baseVertexLocation, 0);
	}



	m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_frameIndex].Get(), 
			D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));
	
	ThrowIfFailed(m_commandList->Close());
}

void Renderer::FlushCommandQueue()
{
	m_fenceValue++;

	ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), m_fenceValue));

	if (m_fence->GetCompletedValue() < m_fenceValue)
	{
		ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValue, m_fenceEvent));
		WaitForSingleObject(m_fenceEvent, INFINITE);
	}
}

void Renderer::CalculateFrameStats()
{
	static int frameCnt = 0;
	static float timeElapsed = 0.0f;

	frameCnt++;

	// Compute averages over one second period.
	if ( m_timer->TotalTime() - timeElapsed  >= 1.0f)
	{
		float fps = (float)frameCnt; // fps = frameCnt / 1
		float mspf = 1000.0f / fps;

		std::wstring fpsStr = std::to_wstring(fps);
		std::wstring mspfStr = std::to_wstring(mspf);

		std::wstring windowText = L" fps: " + fpsStr +
			L"  mspf: " + mspfStr;

		SetCustomWindowText(windowText.c_str());

		// Reset for next average.
		frameCnt = 0;
		timeElapsed += 1.0f;
	}
}

std::array<const CD3DX12_STATIC_SAMPLER_DESC, 6> 
Renderer::GetStaticSamplers()
{
	// Applications usually only need a handful of samplers.  So just define them all up front
	// and keep them available as part of the root signature.  

	const CD3DX12_STATIC_SAMPLER_DESC pointWrap(
		0, // shaderRegister
		D3D12_FILTER_MIN_MAG_MIP_POINT, // filter
		D3D12_TEXTURE_ADDRESS_MODE_WRAP,  // addressU
		D3D12_TEXTURE_ADDRESS_MODE_WRAP,  // addressV
		D3D12_TEXTURE_ADDRESS_MODE_WRAP); // addressW

	const CD3DX12_STATIC_SAMPLER_DESC pointClamp(
		1, // shaderRegister
		D3D12_FILTER_MIN_MAG_MIP_POINT, // filter
		D3D12_TEXTURE_ADDRESS_MODE_CLAMP,  // addressU
		D3D12_TEXTURE_ADDRESS_MODE_CLAMP,  // addressV
		D3D12_TEXTURE_ADDRESS_MODE_CLAMP); // addressW

	const CD3DX12_STATIC_SAMPLER_DESC linearWrap(
		2, // shaderRegister
		D3D12_FILTER_MIN_MAG_MIP_LINEAR, // filter
		D3D12_TEXTURE_ADDRESS_MODE_WRAP,  // addressU
		D3D12_TEXTURE_ADDRESS_MODE_WRAP,  // addressV
		D3D12_TEXTURE_ADDRESS_MODE_WRAP); // addressW

	const CD3DX12_STATIC_SAMPLER_DESC linearClamp(
		3, // shaderRegister
		D3D12_FILTER_MIN_MAG_MIP_LINEAR, // filter
		D3D12_TEXTURE_ADDRESS_MODE_CLAMP,  // addressU
		D3D12_TEXTURE_ADDRESS_MODE_CLAMP,  // addressV
		D3D12_TEXTURE_ADDRESS_MODE_CLAMP); // addressW

	const CD3DX12_STATIC_SAMPLER_DESC anisotropicWrap(
		4, // shaderRegister
		D3D12_FILTER_ANISOTROPIC, // filter
		D3D12_TEXTURE_ADDRESS_MODE_WRAP,  // addressU
		D3D12_TEXTURE_ADDRESS_MODE_WRAP,  // addressV
		D3D12_TEXTURE_ADDRESS_MODE_WRAP,  // addressW
		0.0f,                             // mipLODBias
		8);                               // maxAnisotropy

	const CD3DX12_STATIC_SAMPLER_DESC anisotropicClamp(
		5, // shaderRegister
		D3D12_FILTER_ANISOTROPIC, // filter
		D3D12_TEXTURE_ADDRESS_MODE_CLAMP,  // addressU
		D3D12_TEXTURE_ADDRESS_MODE_CLAMP,  // addressV
		D3D12_TEXTURE_ADDRESS_MODE_CLAMP,  // addressW
		0.0f,                              // mipLODBias
		8);                                // maxAnisotropy

	return {
		pointWrap, pointClamp,
		linearWrap, linearClamp,
		anisotropicWrap, anisotropicClamp };
}